package fr.umlv.tatoo.runtime.tools.builder;

import java.io.Reader;
import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import fr.umlv.tatoo.runtime.buffer.LexerBuffer;
import fr.umlv.tatoo.runtime.buffer.LocationProvider;
import fr.umlv.tatoo.runtime.buffer.TokenBuffer;
import fr.umlv.tatoo.runtime.buffer.impl.LocationTracker;
import fr.umlv.tatoo.runtime.buffer.impl.ReaderWrapper;
import fr.umlv.tatoo.runtime.lexer.DefaultLexerErrorRecoveryPolicy;
import fr.umlv.tatoo.runtime.lexer.DefaultLexerWarningReporter;
import fr.umlv.tatoo.runtime.lexer.DiscardLexerErrorForwarder;
import fr.umlv.tatoo.runtime.lexer.Lexer;
import fr.umlv.tatoo.runtime.lexer.LexerErrorForwarder;
import fr.umlv.tatoo.runtime.lexer.LexerErrorRecoveryPolicy;
import fr.umlv.tatoo.runtime.lexer.LexerListener;
import fr.umlv.tatoo.runtime.lexer.LexerTable;
import fr.umlv.tatoo.runtime.lexer.LexerWarningReporter;
import fr.umlv.tatoo.runtime.lexer.LexingException;
import fr.umlv.tatoo.runtime.lexer.LifecycleHandler;
import fr.umlv.tatoo.runtime.lexer.NoLexerErrorRecoveryPolicy;
import fr.umlv.tatoo.runtime.lexer.RuleActivator;
import fr.umlv.tatoo.runtime.lexer.SimpleLexer;
import fr.umlv.tatoo.runtime.parser.BranchingParserListener;
import fr.umlv.tatoo.runtime.parser.DefaultParserErrorRecoveryPolicy;
import fr.umlv.tatoo.runtime.parser.DefaultParserWarningReporter;
import fr.umlv.tatoo.runtime.parser.LookaheadMap;
import fr.umlv.tatoo.runtime.parser.Parser;
import fr.umlv.tatoo.runtime.parser.ParserErrorRecoveryListener;
import fr.umlv.tatoo.runtime.parser.ParserErrorRecoveryPolicy;
import fr.umlv.tatoo.runtime.parser.ParserListener;
import fr.umlv.tatoo.runtime.parser.ParserTable;
import fr.umlv.tatoo.runtime.parser.ParserWarningReporter;
import fr.umlv.tatoo.runtime.parser.SimpleParser;
import fr.umlv.tatoo.runtime.tools.AnalyzerListener;
import fr.umlv.tatoo.runtime.tools.Debug;
import fr.umlv.tatoo.runtime.tools.LookaheadMapFactory;
import fr.umlv.tatoo.runtime.tools.ParserForwarder;
import fr.umlv.tatoo.runtime.tools.ParserLookaheadActivator;
import fr.umlv.tatoo.runtime.tools.SingleVersionParserLookaheadActivator;
import fr.umlv.tatoo.runtime.tools.ToolsListener;
import fr.umlv.tatoo.runtime.tools.ToolsProcessor;
import fr.umlv.tatoo.runtime.tools.ToolsTable;
import fr.umlv.tatoo.runtime.util.Utils;

/** This class allows to easily create a lexer, a parser or an analyzer (lexer+parser)
 *  using a fluent API.   
 *  
 *  <ol>
 *    <li>Analyzer
 *   <ol>
 *  <li>Run an analyzer 
 *  <pre>
 *  TerminalEvaluator&lt;CharSequence&gt; terminalEvaluator = ...
 *  GrammarEvaluator grammarEvaluator = ...
 *
 *  Reader reader = ... 
 *  Analyzer.run(reader, terminalEvaluator, grammarEvaluator, null, null);
 *  </pre>
 *   
 *  Note that the class {@code Analyzer} is the one generated by Tatoo tools.
 *  This class also provides several static methods {@code run}/{@code runDebug}
 *  to run/debug the analyzer. 
 *   
 *  <li>Create a configurable analyzer:
 *  
 *  <pre>
 *  Analyzer#analyzer().
 *    {@link AnalyzerTableBuilder#reader(Reader) reader}(reader).
 *    {@link AnalyzerBufferBuilder#listener(AnalyzerListener) listener}(analyzerListener).
 *    {@link AnalyzerBuilder#createAnalyzer() create}();
 *  </pre>
 *  
 *  <li>Create a configurable analyzer without using class <tt>Analyzer</tt>
 *  
 *  <pre>
 *  Builder.{@link Builder#analyzer(LexerTable, ParserTable, ToolsTable) analyzer}(lexerTable, parserTable, toolsTable).
 *    {@link AnalyzerTableBuilder#reader(Reader) reader}(reader).
 *    {@link AnalyzerBufferBuilder#listener(AnalyzerListener) listener}(analyzerListener).
 *    {@link AnalyzerBuilder#createAnalyzer() create}();
 *  </pre>
 *  
 *  <li>Create a configurable analyzer with a tools listener:
 *  <pre>
 *  ToolsListener&lt;...&gt; toolsListener=...
 *  Builder.{@link Builder#analyzer(LexerTable, ParserTable, ToolsTable) analyzer}(lexerTable, parserTable, toolsTable).
 *    {@link AnalyzerTableBuilder#reader(Reader) reader}(reader).
 *    {@link AnalyzerBufferBuilder#listener(ToolsListener) listener}(toolsListener).
 *    {@link AnalyzerBuilder#createAnalyzer() create}();
 *  </pre>
 *  </ol>
 *  
 *   <li>Lexer
 *  <ul>
 *  <li>Create a simple lexer (with no parser):
 *  <pre>
 *  Builder.{@link Builder#lexer(LexerTable) lexer}(lexerTable).
 *    {@link LexerTableBuilder#reader(Reader) reader}(reader).
 *    {@link LexerBufferBuilder#listener(LexerListener) listener}(lexerListener).
 *    {@link LexerBuilder#createLexer() create}();
 *  </pre>
 *    
 *  <li>Create a simple lexer with a specific buffer:
 *  <pre>
 *  LexerBuffer buffer=
 *  Builder.{@link Builder#lexer(LexerTable) lexer}(lexerTable).
 *    {@link LexerTableBuilder#buffer(LexerBuffer) buffer(buffer)}.
 *    {@link LexerBufferBuilder#listener(LexerListener) listener}(lexerListener).
 *    {@link LexerBuilder#createLexer() create}();
 *  </pre>
 *  
 *  <li>Create a simple lexer with a specific activator:
 *  <pre>
 *  RuleActivator&lt;...&gt; activator=
 *  Builder.{@link Builder#lexer(LexerTable) lexer}(lexerTable).
 *    {@link LexerTableBuilder#reader(Reader) reader}(reader).
 *    {@link LexerBufferBuilder#listener(LexerListener) listener}(lexerListener).
 *    {@link LexerBuilder#activator(RuleActivator) activator}(activator).
 *    {@link LexerBuilder#create create}();
 *  </pre>
 *  
 *  <li>Create a simple lexer with no error recovery:
 *  <pre>
 *  LexerBuffer buffer=
 *  Builder.{@link Builder#lexer(LexerTable) lexer}(lexerTable).
 *    {@link LexerTableBuilder#reader(Reader) reader}(reader).
 *    {@link LexerBufferBuilder#listener(LexerListener) listener}(lexerListener).
 *    {@link LexerBuilder#expert() expert}().
 *    {@link ExpertLexerBuilder#noErrorPolicy() noErrorPolicy}().
 *    {@link LexerBuilder#createLexer() create}();
 *  </pre>
 *  
 *  <li>Create a simple lexer with a specific error recovery policy:
 *  <pre>
 *  LexerErrorRecoveryPolicy&lt;...&gt; lexerErrorPolicy=
 *  Builder.{@link Builder#lexer(LexerTable) lexer}(lexerTable).
 *    {@link LexerTableBuilder#reader(Reader) reader}(reader).
 *    {@link LexerBufferBuilder#listener(LexerListener) listener}(lexerListener).
 *    {@link LexerBuilder#expert() expert}().
 *    {@link ExpertLexerBuilder#errorPolicy(LexerErrorRecoveryPolicy) errorPolicy}(LexerErrorRecoveryPolicy).
 *    {@link LexerBuilder#create create}();
 *  </pre>
 *  
 *  <li>Create a lexer using an existing parser:
 *  <pre>
 *  Parser&lt;...&gt; parser=...
 *  ToolsTable&lt;...&gt; toolsTable=...
 *  Builder.{@link Builder#lexer(LexerTable) lexer}(lexerTable).
 *    {@link LexerTableBuilder#reader(Reader) reader}(reader).
 *    {@link LexerBufferBuilder#listener(LexerListener) listener}(lexerListener).
 *    {@link LexerBuilder#expert() expert}().
 *    {@link ExpertLexerBuilder#parser(Parser, ToolsTable) parser}(parser, toolsTable).
 *    {@link LexerBuilder#createLexer() create}();
 *  </pre>
 *  </ol>  
 *    <li>Parser
 *   <ol>
 *  <li>Create a simple parser (with no lexer):
 *  <pre>
 *  Builder.{@link Builder#parser(ParserTable) parser}(parserTable).
 *    {@link ParserTableBuilder#listener(ParserListener) listener}(parserListener).
 *    {@link ParserBuilder#createParser() create}();
 *  </pre>
 *  
 *  <li>Create a simple parser with a start non terminal and a version:
 *  <pre>
 *  Builder.{@link Builder#parser(ParserTable) parser}(parserTable).
 *    {@link ParserTableBuilder#listener(ParserListener) listener}(parserListener).
 *    {@link ParserBuilder#start(Object) start}(startNonTerminal).
 *    {@link ParserBuilder#version(Object) version}(version).
 *    {@link ParserBuilder#createParser() create}();
 *  </pre>
 *  
 *  <li>Create a simple parser with a no error recovery:
 *  <pre>
 *  Builder.{@link Builder#parser(ParserTable) parser}(parserTable).
 *    {@link ParserTableBuilder#listener(ParserListener) listener}(parserListener).
 *    {@link ParserBuilder#expert() expert}().
 *    {@link ExpertParserBuilder#noErrorPolicy() noErrorPolicy}().
 *    {@link ParserBuilder#createParser() create}();
 *  </pre>
 *  
 *  <li>Create a simple parser with a specific error recovery policy:
 *  <pre>
 *  ParserErrorRecoveryPolicy&lt;...&gt; errorPolicy=...
 *  Builder.{@link Builder#parser(ParserTable) parser}(parserTable).
 *    {@link ParserTableBuilder#listener(ParserListener) listener}(parserListener).
 *    {@link ParserBuilder#expert() expert}().
 *    {@link ExpertParserBuilder#errorPolicy(ParserErrorRecoveryPolicy) errorPolicy}(errorPolicy).
 *    {@link ParserBuilder#createParser() create}();
 *  </pre>
 *  </ol>
 *  
 *  </ul>
 *   
 * @author Remi
 */
public class Builder {
  private Builder() {
    // only static methods
  }
  
  /** Creates a lexer builder from a lexer table.
   * @param <R> type of the rules.
   * @param lexerTable a lexer table
   * @return a lexer builder configured with the lexer table.
   * 
   * @throws IllegalArgumentException if the lexerTable is null.
   */
  public static <R> LexerTableBuilder<R> lexer(LexerTable<R> lexerTable) {
    if (lexerTable==null)
      throw new IllegalArgumentException("lexerTable is null");
    return new LexerTableBuilder<R>(lexerTable);
  }
  
  /** A lexer builder that configures the buffer of the lexer.
   * 
   * @param <R> type of rules.
   */
  public static class LexerTableBuilder<R> {
    private final LexerTable<R> lexerTable;
    
    LexerTableBuilder(LexerTable<R> lexerTable) {
      this.lexerTable=lexerTable;
    }
    
    /** Returns a lexer builder configured with the buffer taken as argument.
     * @param <B> type of buffer.
     * @param buffer a lexer buffer or null if the buffer is provided later to the lexer.
     * @return a lexer builder configured with the buffer taken as argument.
     * 
     * @see Lexer#reset(LexerBuffer)
     */
    public <B extends LexerBuffer> LexerBufferBuilder<R,B> buffer(B buffer) {
      return new LexerBufferBuilder<R,B>(lexerTable,buffer);
    }
    
    /** Returns a lexer builder which used a {@link ReaderWrapper} as
     *   lexer buffer.
     *  The buffer will compute locations in the text using a {@link LocationTracker}.
     * @param reader a reader
     * @return a a lexer builder which used a {@link ReaderWrapper} as
     *   lexer buffer.
     *   
     * @throws IllegalArgumentException if the reader is null
     *   
     * @see #buffer(LexerBuffer)
     */
    public LexerBufferBuilder<R,ReaderWrapper> reader(Reader reader) {
      if (reader==null)
        throw new IllegalArgumentException("reader is null");
      return buffer(new ReaderWrapper(reader,new LocationTracker()));
    }
  }
  
  /** A lexer builder that configure the listener of the lexer.
   *
   * @param <R> type of rules.
   * @param <B> type of buffer.
   */
  public static class LexerBufferBuilder<R,B extends LexerBuffer> {
    private final LexerTable<R> lexerTable;
    private final B buffer;
    
    LexerBufferBuilder(LexerTable<R> lexerTable,B buffer) {
      this.lexerTable=lexerTable;
      this.buffer=buffer;
    }
    
    /** Returns a lexer builder configured with the lexer listener taken as parameter.
     * @param listener a lexer listener
     * @return a lexer builder configured with the lexer listener taken as parameter.
     * 
     * @throws IllegalArgumentException if the lexer listener is null
     * 
     * @see #debugListener()
     */
    public LexerBuilder<R,B> listener(LexerListener<? super R,? super B> listener) {
      if (listener==null) {
        throw new IllegalArgumentException("listener is null");
      }
      return new LexerBuilder<R,B>(lexerTable,buffer,listener);
    }
    
    /** Returns a lexer builder configured with a special implementation of
     *  a lexer listener that will print the events sent by the lexer.
     *  
     * @return a lexer builder configured with a debugging lexer listener.
     *
     * @see #listener(LexerListener)
     */
    public LexerBuilder<R,B> debugListener() {
      return listener(Default.LEXER_LISTENER);
    }
    
    static class Default {
      static final LexerListener<Object,LexerBuffer> LEXER_LISTENER =
        new LexerListener<Object,LexerBuffer>() {

        public void ruleVerified(Object rule, int lastTokenLength, LexerBuffer buffer)
        throws RuntimeException {
          String s="";
          if (buffer instanceof TokenBuffer<?>) {
            s = "("+((TokenBuffer<?>)buffer).view().toString()+") ";
          }
          LocationProvider location = buffer.getLocationProvider();
          if (location!=null) {
            System.err.printf("At %d:%d, token %s %srecognized of length %d%n",
                location.getLineNumber(),location.getColumnNumber(),
                rule,s,lastTokenLength);
          } else {
            System.err.printf("Token %s %srecognized of length %d%n",rule,s,lastTokenLength);
          }
          buffer.discard();
        }

      };
    }
  }
  
  /** Creates a rule activator that activates all rules.
   *  
   * @param table the parser table, never null.
   * 
   * @see LexerTable#getRuleDataMap()
   */
  static <R> RuleActivator<R> createAllRuleActivator(LexerTable<R> table) {
    Set<R> rules = table.getRuleDataMap().keySet();
    if (rules.isEmpty())
      throw new IllegalArgumentException("No rules in table");
    @SuppressWarnings("unchecked")
    final R[] array = rules.toArray((R[])Array.newInstance(rules.iterator().next().getClass(),rules.size()));
    
    return  new RuleActivator<R>() {
      public R[] activeRules() {
        return array;
      }
    };
  }
  
  private static <R> Class<? extends R> guessRuleClass(ToolsTable<R,?> toolsTable) {
    Set<? extends R> set=toolsTable.getDiscardSet();
    if (!set.isEmpty())
      return pickOneItem(set);
    set=toolsTable.getSpawnSet();
    if (!set.isEmpty())
      return pickOneItem(set);
    set=toolsTable.getUnconditionalRuleSet();
    if (!set.isEmpty())
      return pickOneItem(set);
    set=toolsTable.getRuleToTerminalMap().keySet();
    if (!set.isEmpty())
      return pickOneItem(set);
    throw new IllegalStateException("no rule defined");
  }
  
  @SuppressWarnings("unchecked")
  private static <R> Class<? extends R> pickOneItem(Set<? extends R> set) {
    //FIXME Remi: why cast is needed, need investigation
    return (Class<? extends R>)set.iterator().next().getClass();
  }
  
  /** Creates a lexer rule activator that activate rules depending on the parser state. 
   * @param <R> type of rules.
   * @param <T> type of terminals. 
   * @param <V> type of versions.
   * @param parser a parser.
   * @param toolsTable a tools table used to compute the set of rules
   *   corresponding to a terminal
   * @return a lexer rule activator.
   */
  static <R,T,V> RuleActivator<R> createLookahedRuleActivator(Parser<T,?,?,V> parser,ToolsTable<R,T> toolsTable) {
    ParserTable<T, ?, ?, V> parserTable=parser.getTable();
    Class<?> ruleClass = guessRuleClass(toolsTable);
    
    if (parserTable.getVersions().size() == 1) {
      // specialized class if there is only one grammar version 
      return SingleVersionParserLookaheadActivator.create(parser, toolsTable, ruleClass);
    }
    return ParserLookaheadActivator.create(parser, toolsTable, ruleClass);
  }
  
  /** A lexer builder that is able to set an activator,
   *  create a lexer or create a new builder in expert mode.
   *
   * @param <R> type of rules.
   * @param <B> type of the buffer.
   */
  public static class LexerBuilder<R,B extends LexerBuffer> {
    private final LexerTable<R> lexerTable;
    private final B buffer;
    private LexerListener<? super R,? super B> listener;
    private RuleActivator<R> activator;
    
    LexerBuilder(LexerTable<R> lexerTable,B buffer,
        LexerListener<? super R,? super B> listener) {
      this.lexerTable=lexerTable;
      this.buffer=buffer;
      this.listener=listener;
    }
    
    /** Set a rule activator that will activate all rules
     *  at any time.
     * @return this builder.
     * 
     * @see #activator(RuleActivator)
     */
    public LexerBuilder<R,B> allRuleActivator() {
      return activator(createAllRuleActivator(lexerTable));
    }
    
    /** Set a rule activator.
     *  A rule activator is called by the lexer each time a
     *  new token is about to be recognized.
     *  The rule activator returns the rule that can be used
     *  by the lexer in that context.  The context is not provided
     *  by the lexer, it's up to the rule activator to maintain
     *  such context.
     *  
     * @param activator a rule activator
     * @return this builder.
     */
    public LexerBuilder<R,B> activator(RuleActivator<R> activator) {
      this.activator=activator;
      return this;
    }
    
    /** Adds a debug proxy in front of the lexer listener
     *  that will trace all calls to the lexer listener 
     * @return a lexer builder with a lexer listener wrapped by a trace proxy.
     */
    @SuppressWarnings("unchecked") // generics aren't reified
    public LexerBuilder<R,B> trace() {
      listener=Debug.createTraceProxy(LexerListener.class,listener);
      return this;
    }
    
    /** Returns a lexer builder with more configuration options.
     *  If no rule activator was previously set, a rule activator
     *  that activate all rules will be configured.
     * @return a lexer builder with more configuration options.
     */
    public ExpertLexerBuilder<R,B> expert() {
      return new ExpertLexerBuilder<R,B>(lexerTable,buffer,listener,activator);  
    }
    
    /** Creates a lexer with all rule activated if no activator is provided,
     *  no error handling policy and no lifecycle handler.
     *  
     *  @see #create()
     *  @see #allRuleActivator()
     *  @see ExpertLexerBuilder#noErrorPolicy()
     *  @see ExpertLexerBuilder#lifecycleHandler(LifecycleHandler)
     */
    public Lexer<B> createLexer() {
      return expert().createLexer();
    }
    
    /** Creates a simple lexer.
     *  The lexer is configured with all rule activated if no activator is provided,
     *  the discard error handling policy and no lifecycle handler.
     *  
     *  @see #createLexer()
     */
    public SimpleLexer create() {
      return createLexer();
    }
  }
  
  static abstract class AbstractExpertLexerBuilder<R,B extends LexerBuffer,S extends AbstractExpertLexerBuilder<R,B,S>> {
    /** Set a rule activator.
     *  A rule activator is called by the lexer each time a
     *  new token is about to be recognized.
     *  The rule activator returns the rule that can be used
     *  by the lexer in that context.  The context is not provided
     *  by the lexer, it's up to the rule activator to maintain
     *  a way to get such context.
     *  
     * @param activator a rule activator
     * @return the current builder.
     */
    public abstract S activator(RuleActivator<R> activator);
    
    /** Returns the default rule activator.
     *  This method can be used to wrap the default rule activator and
     *  reinject the new rule activator using {@link #activator(RuleActivator)}.
     * @return the default rule activator
     */
    public abstract RuleActivator<R> getDefaultActivator();
    
    /** Configure a lifecycle handler for the lexer.
     * @param lifecycleHandler a lifecycle handler.
     * @return the current builder.
     */
    public abstract S lifecycleHandler(LifecycleHandler<B> lifecycleHandler);
    
    /** Configure the lexer error recovery policy to throw a lexing
     *  exception if a character is not recognized.    
     * 
     * @return the current builder.
     * 
     * @see #errorPolicy(LexerErrorRecoveryPolicy)
     * @see NoLexerErrorRecoveryPolicy
     */
    public S noErrorPolicy() {
      return errorPolicy(new NoLexerErrorRecoveryPolicy<R,B>());
    }
    
    /** Configure the lexer error recovery policy with the default policy.
     *  The default policy forwards the lexing error to a lexer error forwarder
     *  the return value indicates if the lexer state must be reset
     *  to try to re-lex or if one character must be discarded.
     *  In that case, the lexer warning reporter will be called.
     * 
     * @param forwarder an error forwarder
     * @param lexerWarningReporter a warning reporter called each time a character
     *  is discarded.
     * @return the current builder.
     * 
     * @see #errorPolicy(LexerErrorRecoveryPolicy)
     * @see #discardErrorPolicy(LexerWarningReporter)
     * @see DefaultLexerErrorRecoveryPolicy
     */
    public S discardErrorPolicy(LexerErrorForwarder<B> forwarder,
        LexerWarningReporter<B> lexerWarningReporter) {
      return errorPolicy(
        new DefaultLexerErrorRecoveryPolicy<R,B>(
          forwarder,lexerWarningReporter));
    }
    
    /** Configure the lexer error recovery policy with the default policy.
     *  If an error occurs during the lexing the offending character
     *  is discarded and the lexing process continue with the following character.
     *  Each time a character is discarded, the warning reporter is called
     *  with an error message.
     *  If there is no more character and the lexer is closed
     *  and a LexingException will be thrown.
     *  
     * @param lexerWarningReporter a warning reporter called each time a character
     *  is discarded.
     * @return the current builder
     * 
     * @see #errorPolicy(LexerErrorRecoveryPolicy)
     * @see #discardErrorPolicy(LexerErrorForwarder, LexerWarningReporter)
     * @see DefaultLexerWarningReporter
     */
    public S discardErrorPolicy(LexerWarningReporter<B> lexerWarningReporter) {
      return discardErrorPolicy(DiscardLexerErrorForwarder.<B>discardForwarder(),
        lexerWarningReporter);
    }
    
    /** Configure a pluggable error recovery policy.
     *  This policy will be called when a character is not recognized by any automata
     *  of the lexer.
     * 
     * @param errorPolicy an error recovery policy
     * @return the current builder
     */
    public abstract S errorPolicy(LexerErrorRecoveryPolicy<R, B> errorPolicy);
  }
  
  
  /** A lexer builder that configures the error policy and
   *  lifecycle handler of the lexer.
  *
  * @param <R> type of rules.
  * @param <B> type of the buffer.
  */
  public static class ExpertLexerBuilder<R,B extends LexerBuffer>
    extends AbstractExpertLexerBuilder<R,B,ExpertLexerBuilder<R,B>> {
    
    final LexerTable<R> lexerTable;
    private final B buffer;
    private final LexerListener<? super R,? super B> listener;
    RuleActivator<R> activator;
    LexerErrorRecoveryPolicy<R, B> errorPolicy;
    LifecycleHandler<B> lifecycleHandler;
    
    ExpertLexerBuilder(LexerTable<R> lexerTable,B buffer,
        LexerListener<? super R,? super B> listener,
        RuleActivator<R> activator) {
      this.lexerTable=lexerTable;
      this.buffer=buffer;
      this.listener=listener;
      this.activator=activator;
    }
    
    /**
     * {@inheritDoc}
     * 
     * @return the current lexer builder
     */
    @Override
    public ExpertLexerBuilder<R,B> activator(RuleActivator<R> activator) {
      this.activator=activator;
      return this;
    }
    
    @Override
    public RuleActivator<R> getDefaultActivator() {
      return createAllRuleActivator(lexerTable);
    }
    
    /**
     * {@inheritDoc}
     * 
     * @return the current lexer builder
     */
    @Override
    public ExpertLexerBuilder<R,B> lifecycleHandler(LifecycleHandler<B> lifecycleHandler) {
      this.lifecycleHandler=lifecycleHandler;
      return this;
    }
    
    /**
     * {@inheritDoc}
     * 
     * @return the current lexer builder
     */
    @Override
    public ExpertLexerBuilder<R,B> errorPolicy(LexerErrorRecoveryPolicy<R,B> errorPolicy) {
      this.errorPolicy=errorPolicy;
      return this;
    }
    
    /** Asks to configure the lexer with an existing parser.
     *  The tools table is used to create a rule activator that will depend
     *  on the parser state. 
     *  
     * @param parser a parser.
     * @param toolsTable a tools table or null.
     * @return an analyzer builder that is able to configure the link
     *    between a lexer and a parser.
     * 
     * @param <T> type of terminals.
     * @param <N> type of non-terminals
     * @param <P> type of productions.
     * @param <V> type of versions.
     */
    public <T,N,P,V> AnalyzerParserBuilder<R,B,T,N,P,V> parser(Parser<T,N,P,V> parser, ToolsTable<R,T> toolsTable) {
      return new AnalyzerParserBuilder<R,B,T,N,P,V>(this,toolsTable,parser);
    }
    
    /** Asks to configure the lexer with an existing parser.
     *  Because no tools table is provided, all lexer rules are
     *  always activated, see {@link RuleActivator}.
     * 
     * @param parser a parser.
     * @return an analyzer builder that is able to configure the link
     *    between a lexer and a parser.
     * 
     * @param <T> type of terminals.
     * @param <N> type of non-terminals.
     * @param <P> type of productions.
     * @param <V> type of versions.
     * 
     * @see #parser(Parser, ToolsTable)
     */
    public <T,N,P,V> AnalyzerParserBuilder<R,B,T,N,P,V> parser(Parser<T,N,P,V> parser) {
      return parser(parser,null);
    }
    
    /** Creates a lexer with all values.
     *  If no error recovery policy is set, the policy
     *  "{@link #noErrorPolicy() no error policy}" is used.
     *  This policy throws a {@link LexingException} is no rule is recognized.
     *   
     * @return a new Lexer configured with all options previously set.
     * 
     * @see Lexer#createLexer(LexerTable, LexerBuffer, LexerListener, RuleActivator, LifecycleHandler, LexerErrorRecoveryPolicy)
     */
    public Lexer<B> createLexer() {
      RuleActivator<R> activator=this.activator;
      if (activator==null) {
        activator=getDefaultActivator();
      }
      if (errorPolicy==null) {
        noErrorPolicy();
      }
      return Lexer.<R,B>createLexer(lexerTable,buffer,listener,activator,lifecycleHandler,errorPolicy);
    }
  }
  
  /** Creates a parser builder from a parser table.
   *
   * @param parserTable a parser table.
   * @return a parser builder configured with the parser table
   * @throws IllegalArgumentException if the lexerTable is null.
   * 
   * @param <T> type of terminals.
   * @param <N> type of non-terminals.
   * @param <P> type of productions.
   * @param <V> type of versions.
   */
  public static <T,N,P,V> ParserTableBuilder<T,N,P,V> parser(ParserTable<T,N,P,V> parserTable) {
    if (parserTable==null)
      throw new IllegalArgumentException("parserTable is null");
    return new ParserTableBuilder<T,N,P,V>(parserTable);
  }
  
  /** Parser builder that is able to configure a parser listener.
   * 
   * @param <T> type of terminals.
   * @param <N> type of non-terminals.
   * @param <P> type of productions.
   * @param <V> type of versions.
   */
  public static class ParserTableBuilder<T,N,P,V> {
    private final ParserTable<T,N,P,V> parserTable;
    
    ParserTableBuilder(ParserTable<T,N,P,V> parserTable) {
      this.parserTable=parserTable;
    }
    
    /** Set a debug listener that will trace all calls to parser
     *  listener.
     *  This method is for debugging purpose only.
     * @return a parser builder 
     */
    public ParserBuilder<T,N,P,V> debugListener() {
      return listener(Default.PARSER_LISTENER);
    }
    
    /** Set the parser listener wich will be called by the parser when
     *  a token is shifted, a production is reduced or a start non terminal
     *  is accepted.
     * @param listener a parser listener
     * @return a parser builder
     */
    public ParserBuilder<T,N,P,V> listener(ParserListener<? super T,? super N,? super P> listener) {
      if (listener==null) {
        throw new IllegalArgumentException("listener is null");
      }
      return new ParserBuilder<T,N,P,V>(parserTable,listener);
    }
    
    // inner class used to lazily create the default parser listener
    static class Default {
      final static ParserListener<Object, Object, Object> PARSER_LISTENER = 
        new ParserListener<Object, Object, Object>() {
        public void shift(Object terminal) {
          System.err.println("shifting terminal "+terminal);
        }
        public void reduce(Object production) {
          System.err.println("reducing by production "+production);
        }

        public void accept(Object nonTerminal) {
          System.err.println("accept");
        }
      };
    }
  }
  
  /** A parser builder able to configure the version of the grammar
   *  and the start non terminal that will be used by the parser.
   *  
   *  Furthermore this parser builder allows to easily debug
   *  parsers created with the current builder
   *  using {@link ParserBuilder#trace() trace}()
   *  If this method is called, all calls from a parser
   *  to its parser listener will be logged on the console.
   *  
   * @param <T> type of terminals.
   * @param <N> type of non terminals.
   * @param <P> type of productions.
   * @param <V> type of versions.
   */
  public static class ParserBuilder<T,N,P,V> {
    private final ParserTable<T,N,P,V> parserTable;
    private ParserListener<? super T,? super N,? super P> listener;
    private N start;
    private V version;
    private ParserFactory factory = Parser.FACTORY;
    
    ParserBuilder(ParserTable<T,N,P,V> parserTable,ParserListener<? super T,? super N,? super P> listener) {
      this.parserTable=parserTable;
      this.listener=listener;
    }
    
    public Builder.ParserBuilder<T, N, P, V> setFactory(ParserFactory factory) {
		this.factory = factory;
		return this;
	}
    
    /** Set the start non terminal.
     *  The parser will try to recognized the grammar starting
     *  from this non terminal.
     * @param start a start non terminal.
     * @return this parser builder.
     */
    public ParserBuilder<T,N,P,V> start(N start) {
      this.start=start;
      return this;
    }
    
    /** Set the grammar version.
     *  The parser will use this version of the grammar.
     * @param version a grammar version.
     * @return this parser builder.
     */
    public ParserBuilder<T,N,P,V> version(V version) {
      this.version=version;
      return this;
    }
    
    /** Trace all calls to the parser listener.
     *  This method is for debugging purpose only.
     * @return this parser builder.
     */
    @SuppressWarnings("unchecked") // generics aren't reified
    public ParserBuilder<T,N,P,V> trace() {
      listener=Debug.createTraceProxy(ParserListener.class,listener);
      return this;
    }
    
    /** Returns a parser builder in expert mode.
     * @return a parser builder in expert mode.
     */
    public ExpertParserBuilder<T,N,P,V> expert() {
      N start=this.start;
      if (start==null)
        start=parserTable.getDefaultStart();
      V version=this.version;
      if (version==null)
        version=parserTable.getDefaultVersion();
      
      return new ExpertParserBuilder<T,N,P,V>(parserTable,listener,start,version, factory);
    }
    
    /** Create a new parser.
     *  If no version is set, the default version is used.
     *  If no start non terminal is set, default start non terminal
     *   is used.
     *  If no lookaheadMap is defined, the
     *  {@link ExpertParserBuilder#defaultLookaheadMap() default lookahead map} is used.
     *   
     * @return a new parser.
     * 
     * @see #create()
     * @see ExpertParserBuilder#createParser()
     */
    public Parser<T,N,P,V> createParser() {
      return expert().createParser();
    }
    
    /** Create a simple new parser.
     *  This call is roughly equivalent to a call to {@link #createParser()}.
     *   
     * @return a new simple parser.
     * 
     * @see #createParser()
     */
    public SimpleParser<T> create() {
      return expert().createParser();
    }
  }
 
  /** Creates a lookahead map from a parser table.
   *  If the parser table encodes terminals and version
   *  using enum an enum based lookahead map will be used
   *  a hashmap based otherwise. 
   * 
   * @param parserTable a parser table.
   * @return a lookahead map
   * 
   * @param <T> type of terminals.
   * @param <V> type of versions.
   * 
   * @see LookaheadMapFactory
   */
  static <T,V> LookaheadMap<T,V> createLookaheadMap(ParserTable<T,?,?,V> parserTable) {
    T aTerminal=parserTable.getEof();
    V aVersion=parserTable.getDefaultVersion();
    if (aTerminal.getClass().isEnum() &&
        aVersion.getClass().isEnum()) {
      @SuppressWarnings("unchecked") LookaheadMap<T,V> enumLookaheadMap = (LookaheadMap)LookaheadMapFactory.<Enum,Enum>enumLookaheadMap((ParserTable<Enum,?,?,Enum>)parserTable);
      return enumLookaheadMap;  
    } else {
      return LookaheadMapFactory.hashLookaheadMap(parserTable);
    }
  }
  
  static abstract class AbstractExpertParserBuilder<T,N,P,V,S extends AbstractExpertParserBuilder<T,N,P,V,S>> {
    final ParserTable<T,N,P,V> parserTable;
    
    AbstractExpertParserBuilder(ParserTable<T,N,P,V> parserTable) {
      this.parserTable=parserTable;
    }
    
    /** Configure the parser with no error recovery, warning will be printed
     *  by the parser warning reported taken as argument. 
     * 
     * @param parserWarningReporter null is allowed
     * @return the current expert builder.
     * 
     * @see DefaultParserWarningReporter
     */
    public S noErrorPolicy(ParserWarningReporter<T,N,P,V> parserWarningReporter) {
      return errorPolicy(ParserErrorRecoveryPolicy.<T,N,P,V>getNoErrorRecoveryPolicy(
        parserWarningReporter));
    }
    
    /** Configure the parser with no error recovery.
     * @return the current expert builder.
     */
    public S noErrorPolicy() {
      return noErrorPolicy(null);
    }
    
    /** Configure the parser builder with the
     *  {@link DefaultParserErrorRecoveryPolicy default error recovery}.
     *  The error recovery listener provided will be called to adjust
     *  the semantic stack in case of error.
     *  
     * @param errorRecoveryListener a listener called to adjust semantic stack during error recovery.
     * @return the current builder.
     * 
     * @throws IllegalStateException if the grammar define no token error
     * 
     * @see #errorPolicy(ParserErrorRecoveryPolicy)
     */
    public S defaultErrorPolicy(ParserErrorRecoveryListener<? super T,? super N> errorRecoveryListener) {
      if (parserTable.getErrorTerminal()==null)
        throw new IllegalStateException("not error token defined in the grammar");
      return errorPolicy(new DefaultParserErrorRecoveryPolicy<T,N,P,V>(errorRecoveryListener));
    }
    
    /** Configure the parser builder with a user-defined error recovery taken as argument.
     * @param errorPolicy a user defined parser error recovery policy.
     * @return the current parser builder.
     * 
     * @see #noErrorPolicy(ParserWarningReporter)
     * @see #defaultErrorPolicy(ParserErrorRecoveryListener)
     */
    public abstract S errorPolicy(ParserErrorRecoveryPolicy<T,N,P,V> errorPolicy);
    
    /** Configure the parser builder to use the lookahead map created from the parser table.
     *  This method is the default setting, use {@link #lookaheadMap(LookaheadMap)}
     *  to configure another lookahead map.
     * @return the current parser builder
     * 
     * @see LookaheadMap
     */
    public S defaultLookaheadMap() {
      return lookaheadMap(createLookaheadMap(parserTable));
    }
    
    /** Configure the parser builder with a user specified lookahead map.
     * @param lookaheadMap the lookahead map that will be used by parsers
     *   created by the current parser builder. 
     * @return the current parser builder
     * 
     * @see #defaultLookaheadMap()
     */
    public abstract S lookaheadMap(LookaheadMap<? extends T,? super V> lookaheadMap);
  }
  
  /** A parser builder in "expert mode".
   *  This parser builder allow to set the
   *  {@link ParserErrorRecoveryPolicy error recovery policy}
   *  and {@link LookaheadMap the lookahed map}.
   *  
   *  Error recovery policy can be set to {@link #noErrorPolicy() none},
   *  set to {@link #defaultErrorPolicy(ParserErrorRecoveryListener) default}
   *  in that case a {@link ParserErrorRecoveryListener} must be provided
   *  or configured using
   *  {@link #errorPolicy(ParserErrorRecoveryPolicy) a user-defined policy}.
   *  
   *  Lookahead map can be configured to use the
   *  {@link #defaultLookaheadMap() default lookahed map} or to use
   *  a {@link #lookaheadMap(LookaheadMap) user-defined lookahead map}.
   *
   * @param <T> type of terminals
   * @param <N> type of non-terminals
   * @param <P> type of productions
   * @param <V> type of versions.
   */
  public static class ExpertParserBuilder<T,N,P,V>
    extends AbstractExpertParserBuilder<T,N,P,V,ExpertParserBuilder<T,N,P,V>> {
    
    private final ParserListener<? super T,? super N,? super P> listener;
    private final N start;
    private final V version;
    private final ParserFactory factory;
    ParserErrorRecoveryPolicy<T,N,P,V> errorPolicy;
    LookaheadMap<? extends T,? super V> lookaheadMap;
    
    ExpertParserBuilder(ParserTable<T,N,P,V> parserTable,
        ParserListener<? super T,? super N,? super P> listener,
        N start, V version, ParserFactory factory) {
      super(parserTable);
      this.listener=listener;
      this.start=start;
      this.version=version;
      this.factory=factory;
    }
    
    /** {@inheritDoc}
     * 
     * @return the current parser builder.
     */
    @Override
    public ExpertParserBuilder<T,N,P,V> errorPolicy(ParserErrorRecoveryPolicy<T,N,P,V> errorPolicy) {
      this.errorPolicy=errorPolicy;
      return this;
    }
    
    /** {@inheritDoc}
     * 
     * @return the current parser builder.
     */
    @Override
    public ExpertParserBuilder<T,N,P,V> lookaheadMap(LookaheadMap<? extends T,? super V> lookaheadMap) {
      this.lookaheadMap=lookaheadMap;
      return this;   
    }
    
    /** Create a new parser.
     * 
     *  If no version is set, the default version is used.
     *  If no start non terminal is set, default start non terminal
     *   is used.
     *  If no lookaheadMap is defined, the
     *  {@link #defaultLookaheadMap() default lookahead map} is used.
     *  If no error policy is set, {@link #noErrorPolicy()} is used.
     *   
     * @return a new parser.
     */
    public Parser<T,N,P,V> createParser() {
      if (lookaheadMap==null) {
        defaultLookaheadMap();
      }
      if (errorPolicy==null) {
        noErrorPolicy(); 
      }
      return factory.createParser(parserTable,listener,errorPolicy,start,version,lookaheadMap);
    }
  }
  
  /** Creates an analyzer builder, a builder of the couple lexer+parser with
   *  the communication glue.
   *  
   * @param <R> type of the lexer rules.
   * @param <T> type of the parser terminals.
   * @param <N> type of the parser non-terminals.
   * @param <P> type of the parser productions.
   * @param <V> type of the parser versions.
   * 
   * @param lexerTable the lexer table.
   * @param parserTable the parser table.
   * @param toolsTable the communication glue table. 
   * @return a builder that can configure the creation of an anlyzer.
   */
  public static <R,T,N,P,V> AnalyzerTableBuilder<R,T,N,P,V> analyzer(LexerTable<R> lexerTable, ParserTable<T,N,P,V> parserTable, ToolsTable<R,T> toolsTable) {
    return new AnalyzerTableBuilder<R,T,N,P,V>(lexer(lexerTable),parser(parserTable),toolsTable);
  }
  
  /** This analyzer builder allow to configure the lexer buffer.
   *  The method {@link #buffer(LexerBuffer)} configure the lexer
   *  with any {@link LexerBuffer lexer buffer}.
   *  Note that if you want to not only recognize a grammar but
   *  extract token value from the lexer buffer, the lexer
   *  buffer must be a subtype of {@link TokenBuffer} too.
   *  
   *  Method {@link #reader(Reader)} is a convenient method
   *  if the stream of characters is a java.io.Reader.
   * 
   * @param <R> type of the lexer rules.
   * @param <T> type of the parser terminals.
   * @param <N> type of the parser non-terminals.
   * @param <P> type of the parser productions.
   * @param <V> type of the parser versions.
   */
  public static class AnalyzerTableBuilder<R,T,N,P,V> {
    private final LexerTableBuilder<R> lexerTableBuilder;
    private final ParserTableBuilder<T,N,P,V> parserTableBuilder;
    private final ToolsTable<R,T> toolsTable;
    
    AnalyzerTableBuilder(LexerTableBuilder<R> lexerTableBuilder,
        ParserTableBuilder<T,N,P,V> parserTableBuilder,
        ToolsTable<R,T> toolsTable) {
      this.lexerTableBuilder=lexerTableBuilder;
      this.parserTableBuilder=parserTableBuilder;
      this.toolsTable=toolsTable;
    }
    
    /** Configure the analyzer builder with a buffer taken
     *  as argument.
     * @param <B> type of the lexer buffer.
     * 
     * @param buffer a lexer buffer
     * @return a new analyzer builder with the lexer buffer configured.
     * 
     * @see LexerBuffer
     * @see TokenBuffer
     */
    public <B extends LexerBuffer> AnalyzerBufferBuilder<R,B,T,N,P,V> buffer(B buffer) {
      return new AnalyzerBufferBuilder<R,B,T,N,P,V>(lexerTableBuilder.buffer(buffer),
        parserTableBuilder,toolsTable);
    }
    
    /** Configure the analyzer builder with a buffer (a {@link ReaderWrapper})
     *  that wraps the reader taken as argument.
     *  
     * @param reader a reader which the buffer will be built upon.
     * @return a new analyzer builder with the lexer buffer configured.
     * 
     * @see #buffer(LexerBuffer)
     */
    public AnalyzerBufferBuilder<R,ReaderWrapper,T,N,P,V> reader(Reader reader) {
      return new AnalyzerBufferBuilder<R,ReaderWrapper,T,N,P,V>(lexerTableBuilder.reader(reader),
        parserTableBuilder,toolsTable);
    }
  }
  
  /** An analyzer that is able to configure different kind of listeners.
   *  The comon usage is to call
   *  {@link AnalyzerBufferBuilder#listener(AnalyzerListener) listener(analyzerListener)}
   *  with an {@link AnalyzerListener analyzer listener}.
   *  But it's possible to configure separately the {@link ToolsListener tools listener}
   *  using method
   *  {@link AnalyzerBufferBuilder#listener(ToolsListener) listener(toolsListener)}
   *  and the {@link ParserErrorRecoveryListener error recovery listener}
   *  using method
   *  {@link AnalyzerBufferBuilder#parserErrorRecoveryListener(ParserErrorRecoveryListener)
   *         parserErrorRecoveryListener(parserErrorRecoveryListener)}.
   *
   * @param <R> type of lexer rules.
   * @param <B> type of lexer buffer.
   * @param <T> type of parser terminals.
   * @param <N> type of parser non-terminals.
   * @param <P> type of parser productions.
   * @param <V> type of parser versions.
   */
  public static class AnalyzerBufferBuilder<R,B extends LexerBuffer,T,N,P,V> {
    private final LexerBufferBuilder<R,B> lexerBufferBuiler;
    private final ParserTableBuilder<T,N,P,V> parserTableBuilder;
    private final ToolsTable<R,T> toolsTable;
    private ParserErrorRecoveryListener<? super T,? super N> parserErrorRecoveryListener;
    
    AnalyzerBufferBuilder(LexerBufferBuilder<R,B> lexerBufferBuilder,
        ParserTableBuilder<T,N,P,V> parserTableBuilder,
        ToolsTable<R,T> toolsTable) {
      this.lexerBufferBuiler=lexerBufferBuilder;
      this.parserTableBuilder=parserTableBuilder;
      this.toolsTable=toolsTable;
    }
    
    /** Configure the parser error recovery listener.
     *  The parser of the analyzer will use the {@link DefaultParserErrorRecoveryPolicy default error recovery}
     *  configured this the error recovery taken as argument.
     * @param parserErrorRecoveryListener the parser error recovery listener.
     * @return the current analyzer builder.
     */
    public AnalyzerBufferBuilder<R,B,T,N,P,V> parserErrorRecoveryListener(ParserErrorRecoveryListener<? super T,? super N> parserErrorRecoveryListener) {
      this.parserErrorRecoveryListener=parserErrorRecoveryListener;
      return this;
    }
    
    /** Configure the current analyzer builder with a special listener that log all calls
     *  to methods of {@link AnalyzerListener}.
     * @return an analyzer builder configured with a debug listener.
     * 
     * @see #listener(AnalyzerListener)
     */
    public AnalyzerBuilder<R,B,T,N,P,V> debugListener() {
      return listener(Default.ANALYZER_LISTENER);
    }
    
    /** Configure the current analyzer builder with a {@link ToolsListener tools listener}
     *  taken as argument.
     *  This listener is called when the lexer recognized a token or
     *  when the parser shift/reduce or accept.
     *  
     * @param listener a tools listener.
     * @return an analyzer builder configured with the listener taken as argument.
     * 
     * @see #listener(AnalyzerListener)
     */
    public AnalyzerBuilder<R,B,T,N,P,V> listener(ToolsListener<? super R,? super B,? super T,? super N,? super P> listener) {
      if (listener==null) {
        throw new IllegalArgumentException("listener is null");
      } 
      return new AnalyzerBuilder<R,B,T,N,P,V>(
          lexerBufferBuiler,
          parserTableBuilder,
          toolsTable,
          listener,
          parserErrorRecoveryListener);
    }
    
    /**
     * This call is roughtly equivalent to call
     * {@link #parserErrorRecoveryListener(ParserErrorRecoveryListener) parserErrorRecoveryListener(analyzerListener)}
     * followed by
     * {@link #listener(ToolsListener) listener(analyzerListener)}
     * with the same {@link AnalyzerListener analyzer listener}.
     * 
     * @param listener an analyzer listener.
     * @return an analyzer builder configured with the listener taken as argument.
     */
    public AnalyzerBuilder<R,B,T,N,P,V> listener(AnalyzerListener<? super R,? super B,? super T,? super N,? super P> listener) {
      if (listener==null) {
        throw new IllegalArgumentException("listener is null");
      }
      parserErrorRecoveryListener(listener);
      return listener((ToolsListener<? super R,? super B,? super T,? super N,? super P>)listener);
    }
    
    // class used to lazily load the default analyzer listener
    static class Default {
      static final AnalyzerListener<Object,LexerBuffer,Object,Object,Object> ANALYZER_LISTENER=
        new AnalyzerListener<Object,LexerBuffer,Object,Object,Object>() {
        @Override
        public void accept(Object nonTerminal) {
          System.err.println("accept nonTerminal "+nonTerminal);
        }

        @Override
        public void comment(Object rule,LexerBuffer buffer) {
          String s=extractFromBuffer(buffer);
          LocationProvider location = buffer.getLocationProvider();
          if (location!=null) {
            System.err.printf("At %d:%d, comment %s %s%n",
                location.getLineNumber(),location.getColumnNumber(),
                rule,s);
          } else {
            System.err.printf("Comment %s %s%n",rule,s);
          }
        }

        @Override
        public void reduce(Object production) {
          System.err.println("reduce by production "+production);
        }

        @Override
        public void shift(Object terminal,Object rule,LexerBuffer buffer) {
          String s=extractFromBuffer(buffer);
          LocationProvider location = buffer.getLocationProvider();
          if (location!=null) {
            System.err.printf("At %d:%d, terminal %s rule %s %s%n",
                location.getLineNumber(),location.getColumnNumber(),
                terminal,rule,s);
          } else {
            System.err.printf("Terminal %s rule %s %s%n",terminal, rule,s);
          }
        }

        private String extractFromBuffer(LexerBuffer buffer) {
          if (buffer instanceof TokenBuffer<?>) {
            return "("+((TokenBuffer<?>)buffer).view().toString()+") ";
          }
          return "";
        }

        @Override
        public void popNonTerminalOnError(Object nonTerminal) {
          System.err.println("pop nonTerminal "+nonTerminal+" on error");
        }

        @Override
        public void popTerminalOnError(Object terminal) {
          System.err.println("pop terminal "+terminal+" on error");
        }
      };
    }
  }
  
  /** An analyzer builder able to configure the parser
   *  {@link AnalyzerBuilder#start(Object) start non-terminal}
   *  and the parser grammar
   *  {@link AnalyzerBuilder#version(Object) version}. 
   *
   * @param <R> type of lexer rules.
   * @param <B> type of lexer buffer.
   * @param <T> type of parser terminals.
   * @param <N> type of parser non-terminals.
   * @param <P> type of parser productions.
   * @param <V> type of parser versions.
   */
  public static class AnalyzerBuilder<R,B extends LexerBuffer,T,N,P,V> {
    private final LexerBufferBuilder<R,B> lexerBufferBuilder;
    private final ParserTableBuilder<T,N,P,V> parserTableBuilder;
    private final ToolsTable<R,T> toolsTable;
    private ToolsListener<? super R,? super B,? super T,? super N,? super P> listener;
    private ParserErrorRecoveryListener<? super T,? super N> parserErrorRecoveryListener;
    private N start;
    private V version;
    
    AnalyzerBuilder(LexerBufferBuilder<R,B> lexerBufferBuilder,
        ParserTableBuilder<T,N,P,V> parserTableBuilder,
        ToolsTable<R,T> toolsTable,
        ToolsListener<? super R,? super B,? super T,? super N,? super P> listener,
        ParserErrorRecoveryListener<? super T,? super N> parserErrorRecoveryListener) {
      this.lexerBufferBuilder=lexerBufferBuilder;
      this.parserTableBuilder=parserTableBuilder;
      this.toolsTable=toolsTable;
      this.listener=listener;
      this.parserErrorRecoveryListener=parserErrorRecoveryListener;
    }
    
    /** Configure the start non-terminal.
     * @param start the start non-terminal.
     * @return the current analyzer builder.
     */
    public AnalyzerBuilder<R,B,T,N,P,V> start(N start) {
      this.start=start;
      return this;
    }
    
    /** Configure the grammar version.
     * @param version the grammar version.
     * @return the current analyzer builder.
     */
    public AnalyzerBuilder<R,B,T,N,P,V> version(V version) {
      this.version=version;
      return this;
    }
    
    /** Configure the current builder to log all calls to 
     *  any methods of the registered listeners.
     * @return the current analyzer builder.
     * 
     * @see AnalyzerBufferBuilder#listener(AnalyzerListener)
     */
    @SuppressWarnings("unchecked") // generics aren't reified
    public AnalyzerBuilder<R,B,T,N,P,V> trace() {
      listener=Debug.createTraceProxy(ToolsListener.class,listener);
      parserErrorRecoveryListener=Debug.createTraceProxy(
        ParserErrorRecoveryListener.class,parserErrorRecoveryListener);
      return this;
    }
    
    /** Returns an analyzer builder with more options to configure.
     * @return an analyzer builder with more options to configure.
     */
    public ExpertAnalyzerBuilder<R,B,T,N,P,V> expert() {
      ToolsProcessor<R,B,T,N,P> processor=ToolsProcessor.createProcessor(listener);
      return new ExpertAnalyzerBuilder<R,B,T,N,P,V>(lexerBufferBuilder,
          parserTableBuilder.listener(processor).start(start).version(version).expert(),
          toolsTable,processor,parserErrorRecoveryListener);
    }
    
    /** Creates a lexer and a parser configured with all values previously set
     *  to the builder.
     *  The lexer will be automatically conected to the parser (and vice versa).
     * @return an object containing the lexer and the parser.
     * 
     * @see #create()
     * @see #createLexer()
     */
    public LexerAndParser<B,T,N,P,V> createAnalyzer() {
      return expert().createAnalyzer();
    }

    /** Creates a lexer and parser configured with all values previously set
     *  to the builder, but only the lexer is returned.
     *  The lexer will be automatically connected to the parser (and vice versa).
     *  
     *  This method is roughtly equivalent to a call to {@link #createAnalyzer()}
     *  followed by a call to {@link LexerAndParser#getLexer()}.
     *  
     * @return a lexer configured with a parser.
     * 
     * @see #createAnalyzer()
     * @see #create()
     */
    public Lexer<B> createLexer() {
      return createAnalyzer().getLexer();
    }
    
    /** Creates a lexer and parser configured with all values previously set
     *  to the builder, but only the lexer is returned as a {@link SimpleLexer}.
     *  The lexer will be automatically connected to the parser (and vice versa).
     *  
     *  This method is roughtly equivalent to a call to {@link #createLexer()}.
     *  
     * @return a simple lexer configured with a parser.
     * 
     * @see #createLexer()
     * @see #createAnalyzer()
     */
    public SimpleLexer create() {
      return createLexer();
    }
  }
  
  /** An analyzer builder that allows to configure expert options
   *  of an analyzer.
   *  
   *  This builder can configure the parser error policy using
   *  {@link ExpertAnalyzerBuilder#errorPolicy(ParserErrorRecoveryPolicy)}
   *  and the lookahead map using
   *  {@link ExpertAnalyzerBuilder#lookaheadMap(LookaheadMap)}.
   *  
   *  To configure the lexer part of the analyzer
   *  you have to use anothr analyzer builder using
   *  {@link ExpertAnalyzerBuilder#advanced()}.
   *
   * @param <R> type of lexer rules.
   * @param <B> type of lexer buffer.
   * @param <T> type of parser terminals.
   * @param <N> type of parser non-terminals.
   * @param <P> type of parser productions.
   * @param <V> type of parser versions.
   */
  public static class ExpertAnalyzerBuilder<R,B extends LexerBuffer,T,N,P,V>
    extends AbstractExpertParserBuilder<T,N,P,V,ExpertAnalyzerBuilder<R,B,T,N,P,V>> {
    
    private final LexerBufferBuilder<R,B> lexerBufferBuilder;
    final ExpertParserBuilder<T,N,P,V> expertParserBuilder;
    private final ToolsTable<R,T> toolsTable;
    private final ToolsProcessor<R,B,T,N,P> processor;
    private ParserForwarder<T,B> parserForwarder;
    
    // parser options
    ParserErrorRecoveryListener<? super T,? super N> parserErrorRecoveryListener;
    
    ExpertAnalyzerBuilder(LexerBufferBuilder<R,B> lexerBufferBuilder,
        ExpertParserBuilder<T,N,P,V> expertParserBuilder,
        ToolsTable<R,T> toolsTable,
        ToolsProcessor<R,B,T,N,P> processor,
        ParserErrorRecoveryListener<? super T,? super N> parserErrorRecoveryListener) {
      super(expertParserBuilder.parserTable);
      this.lexerBufferBuilder=lexerBufferBuilder;
      this.expertParserBuilder=expertParserBuilder;
      this.toolsTable=toolsTable;
      this.processor=processor;
      this.parserErrorRecoveryListener=parserErrorRecoveryListener;
    }
    
    /** {@inheritDoc}
     * 
     * @return the current analyzer parser builder
     */
    @Override
    public ExpertAnalyzerBuilder<R,B,T,N,P,V> errorPolicy(
        ParserErrorRecoveryPolicy<T,N,P,V> errorPolicy) {
      expertParserBuilder.errorPolicy(errorPolicy);
      return this;
    }

    /** {@inheritDoc}
     * 
     * @return the current analyzer parser builder
     */
    @Override
    public ExpertAnalyzerBuilder<R,B,T,N,P,V> lookaheadMap(
        LookaheadMap<? extends T,? super V> lookaheadMap) {
      expertParserBuilder.lookaheadMap(lookaheadMap);
      return this;
    }
    
    public BranchAnalyzerBuilder<R,B,T,N,P,V> branches() {
      return new BranchAnalyzerBuilder<R,B,T,N,P,V>(this);
    }
    
    /** Returns an analyzer builder that is able
     *  to configure the lexer part of the analyzer.
     *  
     * @return an analyzer builder that allow to configure
     *  the lexer part of the analyzer.
     */
    public AnalyzerParserBuilder<R,B,T,N,P,V> advanced() {
      if (expertParserBuilder.lookaheadMap==null) {
        expertParserBuilder.defaultLookaheadMap();
      }
      if (expertParserBuilder.errorPolicy==null) {
        if (expertParserBuilder.parserTable.getErrorTerminal()!= null && parserErrorRecoveryListener!=null) {
          expertParserBuilder.defaultErrorPolicy(parserErrorRecoveryListener);
        } else {
          expertParserBuilder.noErrorPolicy(
            new DefaultParserWarningReporter<T,N,P,V>());
        }
      }
      Parser<T,N,P,V> parser=expertParserBuilder.createParser();
      
      LexerListener<R,B> lexerListener=processor.createLexerListener(parser,toolsTable);
      
      ExpertLexerBuilder<R,B> expertLexerBuilder=lexerBufferBuilder.listener(lexerListener).expert();
      return new AnalyzerParserBuilder<R,B,T,N,P,V>(expertLexerBuilder,toolsTable,parser).
        parserForwarder(parserForwarder);
    }
    
    /** Creates a lexer and a parser configured with all values previously set
     *  to the builder.
     * @return an object containing the lexer and the parser.
     */
    public LexerAndParser<B,T,N,P,V> createAnalyzer() {
      return advanced().createAnalyzer();
    }
    
    /** Creates a lexer and parser configured with all values previously set
     *  to the builder, but only the lexer is returned.
     *  The lexer will be automatically connected to the parser (and vice versa).
     *  
     *  This method is roughtly equivalent to a call to {@link #createAnalyzer()}
     *  followed by a call to {@link LexerAndParser#getLexer()}.
     *  
     * @return a lexer configured with a parser.
     * 
     * @see #createAnalyzer()
     * @see #create()
     */
    public Lexer<B> createLexer() {
      return createAnalyzer().getLexer();
    }
    
    /** Creates a lexer and parser configured with all values previously set
     *  to the builder, but only the lexer is returned as a {@link SimpleLexer}.
     *  The lexer will be automatically connected to the parser (and vice versa).
     *  
     *  This method is roughtly equivalent to a call to {@link #createLexer()}.
     *  
     * @return a simple lexer configured with a parser.
     * 
     * @see #createLexer()
     * @see #createAnalyzer()
     */
    public SimpleLexer create() {
      return createLexer();
    }
  }
  
  public static class BranchAnalyzerBuilder<R,B extends LexerBuffer,T,N,P,V> {
    private final ExpertAnalyzerBuilder<R,B,T,N,P,V> expertAnalyzerBuilder;
    private final Map<T,Link<?,?>> linkMap;
    
    BranchAnalyzerBuilder(ExpertAnalyzerBuilder<R,B,T,N,P,V> expertAnalyzerBuilder) {
      this.expertAnalyzerBuilder=expertAnalyzerBuilder;
      this.linkMap=new HashMap<T,Link<?,?>>();
    }
    
    static class Link<N,V> {
      private final BranchAnalyzerBuilder<?,?,?,N,?,V> analyzerBuilder;
      private final N start;
      private final V version;
      
      Link(BranchAnalyzerBuilder<?,?,?,N,?,V> analyzerBuilder, N start, V version) {
        this.analyzerBuilder=analyzerBuilder;
        this.start=start;
        this.version=version;
      }

      ResolvedLink<N,V> resolve(Map<BranchAnalyzerBuilder<?,?,?,?,?,?>,LexerAndParser<?,?,?,?,?>> substMap) {
        return new ResolvedLink<N,V>(analyzerBuilder.resolve(substMap),start,version);
      }
    }
    
    static class ResolvedLink<N,V> {
      private final LexerAndParser<?,?,N,?,V> lexerAndParser;
      private final N start;
      private final V version;
      
      ResolvedLink(LexerAndParser<?,?,N,?,V> lexerAndParser, N start, V version) {
        this.lexerAndParser=lexerAndParser;
        this.start=start;
        this.version=version;
      }
      
      void enter() {
        lexerAndParser.getParser().push(start,version);
        try {
          lexerAndParser.getLexer().run();
        } catch(ExitLexing e) {
          // reduce are done by exit
        }
        System.out.println("exit enter");
      }
    }
    
    
    LexerAndParser<B,T,N,P,V> resolve(Map<BranchAnalyzerBuilder<?,?,?,?,?,?>,LexerAndParser<?,?,?,?,?>> substMap) {
      @SuppressWarnings("unchecked")
      LexerAndParser<B,T,N,P,V> lexerAndParser=
        (LexerAndParser<B,T,N,P,V>)substMap.get(this);
      if (lexerAndParser!=null)
        return lexerAndParser;
      
      lexerAndParser=expertAnalyzerBuilder.createAnalyzer();
      substMap.put(this,lexerAndParser);
      
      Class<?> terminalClass=getParserTable().getEof().getClass();
      final Map<T,ResolvedLink<?,?>> map=Utils.createMap(terminalClass);
      
      for(Map.Entry<T,Link<?,?>> entry:this.linkMap.entrySet()) {
        map.put(entry.getKey(),entry.getValue().resolve(substMap));
      }
      
      lexerAndParser.getParser().setBranchingParserListener(new BranchingParserListener<T>() {
        public boolean enter(T terminal) {
          ResolvedLink<?,?> link=map.get(terminal);
          System.out.println("enter "+terminal+" link "+link);
          
          if (link==null)
            return false;

          link.enter();
          return true;
        }
        @Override
        public void exit() {
          throw EXIT_LEXING;
        }
      });
      return lexerAndParser;
    }
    
    //control exception, used to stop lexing
    static class ExitLexing extends RuntimeException {
      private static final long serialVersionUID = 1L;  
    }
    static final ExitLexing EXIT_LEXING=new ExitLexing();
    
    private ParserTable<T,N,P,V> getParserTable() {
      return expertAnalyzerBuilder.expertParserBuilder.parserTable;
    }
    
    public BranchAnalyzerBuilder<R,B,T,N,P,V> link(T terminal, BranchAnalyzerBuilder<?,?,?,?,?,?> analyzerBuilder) {
      return captureLink(terminal,analyzerBuilder);
    }
    
    // just for capture
    private <N2> BranchAnalyzerBuilder<R,B,T,N,P,V> captureLink(T terminal, BranchAnalyzerBuilder<?,?,?,N2,?,?> analyzerBuilder) {
      return captureLink(terminal,analyzerBuilder,
          analyzerBuilder.getParserTable().getDefaultStart());
    }
    
    public <N2> BranchAnalyzerBuilder<R,B,T,N,P,V> link(T terminal, BranchAnalyzerBuilder<?,?,?,N2,?,?> analyzerBuilder, N2 start) {
      return captureLink(terminal,analyzerBuilder,start);
    }
    
    // just for capture
    private <N2,V2> BranchAnalyzerBuilder<R,B,T,N,P,V> captureLink(T terminal, BranchAnalyzerBuilder<?,?,?,N2,?,V2> analyzerBuilder, N2 start) {
      return link(terminal,analyzerBuilder,start,
          analyzerBuilder.getParserTable().getDefaultVersion());
    }
    
    public <N2,V2> BranchAnalyzerBuilder<R,B,T,N,P,V> link(T terminal, BranchAnalyzerBuilder<?,?,?,N2,?,V2> analyzerBuilder, N2 start, V2 version) {
      if (analyzerBuilder==null)
        throw new IllegalArgumentException("analyzerBuilder is null");
      if (start==null)
        throw new IllegalArgumentException("start is null");
      if (version==null)
        throw new IllegalArgumentException("version is null");
      
      linkMap.put(terminal,new Link<N2,V2>(analyzerBuilder, start,version));
      return this;
    }
    
    public LexerAndParser<B,T,N,P,V> createAnalyzer() {
      HashMap<BranchAnalyzerBuilder<?,?,?,?,?,?>,LexerAndParser<?,?,?,?,?>> substMap=
        new HashMap<BranchAnalyzerBuilder<?,?,?,?,?,?>,LexerAndParser<?,?,?,?,?>>();
      return resolve(substMap);
    }
  }
  
 /** This builder allow to configure the lexer options of an analyzer. 
  * 
  * @param <R> type of lexer rules.
  * @param <B> type of lexer buffer.
  * @param <T> type of parser terminals.
  * @param <N> type of parser non-terminals.
  * @param <P> type of parser productions
  * @param <V> type of grammar versions.
  */
  public static class AnalyzerParserBuilder<R,B extends LexerBuffer,T,N,P,V>
    extends AbstractExpertLexerBuilder<R,B,AnalyzerParserBuilder<R,B,T,N,P,V>> {
    
    private final ExpertLexerBuilder<R,B> expertLexerBuilder;
    private final ToolsTable<R,T> toolsTable;
    final Parser<T,N,P,V> parser;
    private ParserForwarder<T,B> parserForwarder;
    private LexerWarningReporter<B> lexerWarningReporter;
    
    AnalyzerParserBuilder(ExpertLexerBuilder<R,B> expertLexerBuilder,
        /*may be null*/ ToolsTable<R,T> toolsTable,
        Parser<T,N,P,V> parser) {
      this.parser=parser;
      this.expertLexerBuilder=expertLexerBuilder;
      this.toolsTable=toolsTable;
    }
    
    /** Returns the current parser.
     * @return the current parser.
     */
    public Parser<T,N,P,V> getParser() {
      return parser;
    }
    
    /** Configure to use a user defined parser forwarder which forward lexer error
     *  and lexer reset/close action to the parser.
     *  
     * @param parserForwarder the user defined parser
     * @return the current analyzer builder
     * 
     * @see #forwardLexerErrorPolicy()
     * @see #lifecycleHandler(LifecycleHandler)
     */
    public AnalyzerParserBuilder<R,B,T,N,P,V> parserForwarder(ParserForwarder<T,B> parserForwarder) {
      this.parserForwarder=parserForwarder;
      return this;
    }
    
    /** Configure the lexer error policy to forward lexer errors to the parser.
     *  This is the default behavior.
     * @return the current analyzer builder.
     * 
     * @see #parserForwarder(ParserForwarder)
     * @see #errorPolicy(LexerErrorRecoveryPolicy)
     */
    public AnalyzerParserBuilder<R,B,T,N,P,V> forwardLexerErrorPolicy() {
      expertLexerBuilder.errorPolicy(null);
      return this;
    }
    
    /** Configure the lexer warning reporter wich is used by the forward lexer error policy
     *  if the parser can't recover the error.
     *  This parameter is not used if the lexer error recovery is different from the
     *  forward lexer error recovery.
     *  
     * @param lexerWarningReporter a lexer warning reporter
     * @return the current analyzer builder.
     * 
     * @see #forwardLexerErrorPolicy()
     */
    public AnalyzerParserBuilder<R,B,T,N,P,V> lexerWarningReporter(LexerWarningReporter<B> lexerWarningReporter) {
      this.lexerWarningReporter = lexerWarningReporter;
      return this;
    }
    
    /**
     * {@inheritDoc}
     * 
     * @return the current analyzer lexer builder
     */
    @Override
    public AnalyzerParserBuilder<R,B,T,N,P,V> activator(RuleActivator<R> activator) {
      expertLexerBuilder.activator(activator);
      return this;
    }
    
    @Override
    public RuleActivator<R> getDefaultActivator() {
      if (toolsTable!=null) {
        return createLookahedRuleActivator(parser,toolsTable);
      } else {
        return createAllRuleActivator(expertLexerBuilder.lexerTable);
      }
    }
    
    /**
     * {@inheritDoc}
     * 
     * Beware, it replace the default lifecycle handler which
     *  is needed to {@link Parser#close() close} or
     *  {@link Parser#reset() reset} the parser when the lexer
     *  is {@link Lexer#close() closed} or
     *  {@link Lexer#reset(LexerBuffer) reset}.
     *  
     *  Because by default, the lexer forwards lexer errors
     *  to the parser too, to intercept communications between
     *  the lexer and the parser the preferred way is to change the 
     *  {@link #parserForwarder(ParserForwarder) parser forwarder}.
     * 
     * @return the current analyzer lexer builder
     */
    @Override
    public AnalyzerParserBuilder<R,B,T,N,P,V> lifecycleHandler(LifecycleHandler<B> lifecycleHandler) {
      expertLexerBuilder.lifecycleHandler(lifecycleHandler);
      return this;
    }
    
    /**
     * {@inheritDoc}
     * 
     * Beware, replacing the lexer error policy will remove the
     * the default glue that transfer all lexing error to the parser.
     * 
     * @return the current analyzer lexer builder
     * 
     * @see DefaultLexerErrorRecoveryPolicy
     */
    @Override
    public AnalyzerParserBuilder<R,B,T,N,P,V> errorPolicy(LexerErrorRecoveryPolicy<R,B> errorPolicy) {
      expertLexerBuilder.errorPolicy(errorPolicy);
      return this;
    }
    
    /** Creates a lexer and a parser configured with all values previously set
     *  to the builder.
     * @return an object containing the lexer and the parser.
     */
    public LexerAndParser<B,T,N,P,V> createAnalyzer() {
      // create lexer
      RuleActivator<R> activator=expertLexerBuilder.activator;
      if (activator==null) {
        expertLexerBuilder.activator(getDefaultActivator());  
      }
          
      ParserForwarder<T,B> parserForwarder=this.parserForwarder;
      if (parserForwarder==null) {
        parserForwarder=new ParserForwarder<T,B>(parser);
      }
      
      LifecycleHandler<B> lifecycleHandler=expertLexerBuilder.lifecycleHandler;
      if (lifecycleHandler==null) {
        lifecycleHandler=parserForwarder;
      }
      expertLexerBuilder.lifecycleHandler(lifecycleHandler);
      
      LexerErrorRecoveryPolicy<R,B> lexerErrorPolicy=expertLexerBuilder.errorPolicy;
      if (lexerErrorPolicy == null) {
        // if there is a error terminal, forward the error to the parser
        // else throw a lexing exception
        if (parser.getTable().getErrorTerminal() == null) {
          lexerErrorPolicy=new NoLexerErrorRecoveryPolicy<R, B>();
        } else {
          lexerErrorPolicy=new DefaultLexerErrorRecoveryPolicy<R,B>(
              parserForwarder, new DefaultLexerWarningReporter<B>());
        }
      }
      expertLexerBuilder.errorPolicy(lexerErrorPolicy);
      
      final Lexer<B> lexer=expertLexerBuilder.createLexer();
      return new LexerAndParser<B,T,N,P,V>() {
        public Lexer<B> getLexer() {
          return lexer;
        }
        public Parser<T,N,P,V> getParser() {
          return parser;
        }
      };
    }
  }
}
